typedef struct omp2ath_spawn_args {
  void (*fn)(void *);
  void *dat;
  long dev;
} omp2ath_spawn_args;

#define TASK_SERVER_END ((void *)(-1L))
#ifdef __sw_host__
#include <stddef.h>
#include <athread.h>
extern void slave_omp2ath_task_runner(void *);
extern void slave_omp2ath_task_server();
extern int __real___real_athread_spawn(void (*)(void), void *, int);
extern void slave_omp2ath_print_perf(long id);
extern volatile omp2ath_spawn_args __omp2ath_args;
// int __omp2ath_pchits[4 * 1024 * 1024];
// extern volatile long __omp2ath_cntrs[8];
// void acc2ath_parallel(int a, void *fn, int b, void *dat, void *sz, void
// *kinds, int c) {
static int initialized = 0;
static int in64 = 0;
static int enable_prof;
void omp2ath_run_slave_task_sync(void(*fn), void *dat, int dev) {
  volatile omp2ath_spawn_args *slave_arg =
      (volatile omp2ath_spawn_args *)SPEIO_LDM_ADDR(
          current_array_id(), 0,
          ((unsigned long)(&__omp2ath_args) - 0x500000004000UL));
  slave_arg->dev = dev;
  slave_arg->dat = dat;
  slave_arg->fn = fn;
  int cgid = current_array_id();
  while (slave_arg->fn != NULL) {
    // long pc = SPEIO_READ_SINGLE_REG(cgid, 0, (SPE_IBXIO_GPC));
    // long pcoff = pc - 0x4ffff0410000L;
    // if (pcoff < 0 || pcoff >= 16 * 1024 * 1024)
    //   printf("incred pc: %p %p\n", pc, pcoff);
    // __omp2ath_pchits[pcoff >> 2]++;
  }
}
void omp2ath_print_perf_(long *id) {
  // int cgid = current_array_id();
  // int speid = *id;
  // volatile long *slave_cntrs = (volatile long *)SPEIO_LDM_ADDR(
  //     cgid, speid,
  //     ((unsigned long)(&__omp2ath_cntrs) - 0x500000004000UL));
  // printf("%14ld %14ld %14ld %14ld %14ld %14ld %14ld %14ld\n", slave_cntrs[0],
  //        slave_cntrs[1], slave_cntrs[2], slave_cntrs[3], slave_cntrs[4],
  //        slave_cntrs[5], slave_cntrs[6], slave_cntrs[7]);
}
void omp2ath_task_server_start() {
  if (!initialized) {
    athread_init();
    initialized = 1;
  }
  if (!in64) {
    __real___real_athread_spawn(slave_omp2ath_task_server, NULL, 0);
    in64 = 1;
  }
  // memset(__omp2ath_pchits, 0, sizeof(__omp2ath_pchits));
}
void omp2ath_task_server_end() {
  if (in64) {
    omp2ath_run_slave_task_sync(TASK_SERVER_END, NULL, -1);
    athread_join();
    in64 = 0;
  }
}
void omp2ath_dump_pchits_() {
  // FILE *pcdump = fopen("pcdump.bin", "wb");
  // fwrite(__omp2ath_pchits, 1, sizeof(__omp2ath_pchits), pcdump);
  // fclose(pcdump);
}
void omp2ath_run_task(void(*fn), void *dat, int dev) {
  omp2ath_task_server_start();
  omp2ath_run_slave_task_sync(fn, dat, dev);
}

int __wrap___real_athread_spawn(void(*fn), void *dat, int flush) {
  omp2ath_task_server_end();
  return __real___real_athread_spawn(fn, dat, flush);
}

void omp2ath_target_ext(int a, void *fn, int b, void *dat, void *sz,
                        void *kinds, int c, size_t d, void *args) {
  omp2ath_run_task(fn, dat, a);
}
void acc2ath_parallel_keyed(int a, void *fn, int b, void *dat, void *sz,
                            void *kinds, int c) {
  omp2ath_run_task(fn, dat, a);
}

void get_coreid_(int *id) { *id = -1; }
#endif
#ifdef __sw_slave__
#include <slave.h>
#include <crts.h>
void get_coreid_(int *id) { *id = _MYID; }
extern void omp2ath_run_stack(void (*fn)(void *), void *dat, void *sp);

int GOMP_single_start() { return _MYID == 0; }

void GOMP_barrier() {
  flush_slave_cache();
  athread_syn(ARRAY_SCOPE, 0xffff);
}
// #include "sw9_pcr.h"
extern void omp2ath_waiting_for_task();
void omp2ath_task_server() {
  // int conf[] = {PC0_CYCLE,
  //               PC1_DCACHE_ACCESS,
  //               PC2_DCACHE_MISS,
  //               PC3_CYC_LAUNHC_NONE,
  //               PC4_L1IC_MISSTIME,
  //               PC5_JMP,
  //               PC6_CYC_LAUNHCNONE_BUFFER,
  //               PC7_CYC_DCACHE_CRQ};
  // asm volatile("wcsr %0, %1\n\t" ::"r"(PCRC_ALL), "i"(PCRC));
  // asm volatile("wcsr %0, %1\n\t" ::"r"(((long)conf[0]) << PCR_SEL_OFF),
  //              "i"(PCR0));
  // asm volatile("wcsr %0, %1\n\t" ::"r"(((long)conf[1]) << PCR_SEL_OFF),
  //              "i"(PCR1));
  // asm volatile("wcsr %0, %1\n\t" ::"r"(((long)conf[2]) << PCR_SEL_OFF),
  //              "i"(PCR2));
  // asm volatile("wcsr %0, %1\n\t" ::"r"(((long)conf[3]) << PCR_SEL_OFF),
  //              "i"(PCR3));
  // asm volatile("wcsr %0, %1\n\t" ::"r"(((long)conf[4]) << PCR_SEL_OFF),
  //              "i"(PCR4));
  // asm volatile("wcsr %0, %1\n\t" ::"r"(((long)conf[5]) << PCR_SEL_OFF),
  //              "i"(PCR5));
  // asm volatile("wcsr %0, %1\n\t" ::"r"(((long)conf[6]) << PCR_SEL_OFF),
  //              "i"(PCR6));
  // asm volatile("wcsr %0, %1\n\t" ::"r"(((long)conf[7]) << PCR_SEL_OFF),
  //              "i"(PCR7));
  omp2ath_waiting_for_task();
}
extern __ldm long __omp2ath_cntrs[8];
void omp2ath_print_perf(long *id) {
  if (_MYID == 0) {
    printf("%ld %ld\n", __omp2ath_cntrs[0], *id);
  }
}
void print_sp_() {
  long val;
  asm("ldi %0, 0($30)\n\t" : "=r"(val));
  printf("sp is at: %p\n", val);
}
//__ldm long t12_trace[16], ra_trace[16];
// void __cyg_profile_func_enter(void *cur, void *from) {
//    for (int i = 15; i >= 1; i --) {
//        t12_trace[i] = t12_trace[i-1];
//        ra_trace[i] = ra_trace[i - 1];
//    }
//    t12_trace[0] = cur;
//    ra_trace[0] = from;
//}
// void __cyg_profile_func_exit(void *cur, void *from) {
//    for (int i = 0; i < 15; i ++) {
//        t12_trace[i] = t12_trace[i + 1];
//        ra_trace[i] = ra_trace[i + 1];
//    }
//}

void omp2ath_hook_void(void *a) {}

#endif
